#pragma once

#include <iostream>
#include <array>
#include <deque>
#include <stack>
#include <bitset>
#include <vector>

namespace BasicSkill
{
    /**
     * 5、基本技巧
     * 5.1.typename关键字
     * 5.2.零值初始化
     * 5.3.使用this->
     * 5.4.原始数组和字符串字面量的模板
     * 5.5.成员模板
     * 5.6.变量模板
     * 5.7.双重模板参数
     */

    /*
    3、使用this
    *   要访问依赖于模板参数的基类成员，必须通过 this-> 或类名访问。
    */
    template <typename T>
    class Base
    {
        template <typename>
        friend class Base; // 省略T
    public:
        void bar()
        {
            std::cout << "bar" << std::endl;
        }

        template <typename T1>
        Base<T> operator=(const Base<T1> &b2);
    };

    template <typename T>
    class Derive : public Base<T>
    {
    public:
        void foo()
        {
            // bar(); // 调用失败，无法直接调用。必须使用this-> 或者 Base<T> 去调用
            this->bar();
            Base<T>::bar();
            std::cout << "foo" << std::endl;
        }
    };

    /*
    4、原始数组和字符串字面量的模板
    */

    // 传入字符字面量的情况
    template <int N, int M>
    void printMN(const char (&a)[N], const char (&b)[M])
    {
        std::cout << "N: " << N << std::endl; // 当传入a="hello"，N=6，结尾是'\0'
        std::cout << "M: " << M << std::endl;
    }

    // 数组所有可能的重载
    template <typename T>
    struct MyClass;

    template <typename T, std::size_t SZ>
    struct MyClass<T[SZ]> // 对已知边界的数组进行特例化
    {
        static void print()
        {
            std::cout << "print MyClass<T[" << SZ << "]>" << std::endl;
        }
    };

    template <typename T, std::size_t SZ>
    struct MyClass<T (&)[SZ]>
    {
        static void print()
        {
            std::cout << "print MyClass<T(&)[" << SZ << "]>" << std::endl;
        }
    };

    template <typename T>
    struct MyClass<T[]> // 对未知边界的数组进行特例化
    {
        static void print()
        {
            std::cout << "print MyClass<T[]>" << std::endl;
        }
    };

    template <typename T>
    struct MyClass<T (&)[]>
    {
        static void print()
        {
            std::cout << "print MyClass<T(&)[]>" << std::endl;
        }
    };

    template <typename T>
    struct MyClass<T *>
    {
        static void print() { std::cout << "print MyClass<T*>" << std::endl; }
    };

    template <typename T1, typename T2, typename T3>
    void foo(int a1[7], int a2[], // pointers by language rules(退化为int*)
             int (&a3)[42],       // ref to array of known bound
             int (&x0)[],         // ref to array of unknown bound
             T1 x1,               // passing by value decays(衰退)
             T2 &x2, T3 &&x3      // passing by ref
    )
    {
        if (std::is_same_v<decltype(a1), int *>)
        { // a1、a2 退化为int*
            std::cout << "yes" << std::endl;
        }
        else
        {
            std::cout << "no" << std::endl;
        }
        // 形参为数组，传入数组，会退化为指针，如a1，a2
        // 形参为数组引用，传入数组，依然是引用，如a3、x0
        MyClass<decltype(a1)>::print();
        MyClass<decltype(a2)>::print();
        MyClass<decltype(a3)>::print();
        MyClass<decltype(x0)>::print();
        MyClass<decltype(x1)>::print();
        MyClass<decltype(x2)>::print();
        MyClass<decltype(x3)>::print(); // x3是万能引用，传入左值引用则x3是左值引用
    }

    /*
    5、成员模板
     */
    // Base的声明见前面，同时为了Base中的成员可见，需要声明为友元
    template <typename T>
    template <typename T1>
    inline Base<T> Base<T>::operator=(const Base<T1> &b2)
    {
        return *this;
    }
    // 成员模板的特化
    class BoolString
    {
    public:
        BoolString(const std::string &val) : value(val) {}
        template <typename T = std::string>
        T get();

    private:
        std::string value;
    };

    template <typename T>
    inline T BoolString::get()
    {
        return value;
    }

    template <>
    bool BoolString::get<bool>()
    {
        return value == "true" || value == "1" || value == "On";
    }

    // 5.5.1 .template构造(不太懂)
    /*
    如果没有使用.template，编译器就不知道后面的小于标记 (<) 是模板参数列表的开头。
    注意，只有在句点之前的构造依赖于模板参数时才会出现问题。
    */
    template <unsigned long N>
    void printBitset(const std::bitset<N> &bs)
    {
        std::cout << bs.template to_string<char, std::char_traits<char>, std::allocator<char>>();
    }

    /**
     * 6、变量模板
     * ps：需要注意的是，对类型模板的改变会在不同文件中同时生效，例如：
     *  template<typename T> T val{};
     *  在a.cpp 中： val<int> = 10; 在 b.cpp 中： std::cout << val<int> << std::endl; // 输出 10
     * ps：对于变量模板，使用时需要带上<>，除非是带有默认值的非类型参数，可以省略<>内的值，但是不能省略<>
     */

    // 使用类型参数进行零值初始化
    template <typename T>
    T val{}; // 零值初始化

    // 使用非类型参数进行初始化(非类型模板参数的限制见nonclassparam.hpp)
    template <auto N = 0>
    decltype(N) dval = N;

    // 部分特化
    template <int N>
    std::array<int, N> arr{};

    /*
    6.1.数据成员的变量模板！！！ is_same<T1,T2>_v、if_enable<>_t等的实现
    */
    template <typename T>
    class MemberVariableTemplate
    {
    public:
        constexpr static int value = 1000;
    };

    template <typename T>
    int MemberVariableTemplate_value = MemberVariableTemplate<T>::value;
    // 调用方式: auto value = MemberVariableTemplate_value<std:string>;
    //      而不是：auto value = MemberVariableTemplate<std:string>::value;

    /*
    6.2.类型特征后缀_v 的实现（since cpp14）
    */
    /*
    // is_const_v 的实现：在type_traits头文件中定义
    template <typename _Tp>
    inline constexpr bool is_const_v = is_const<_Tp>::value;

    // is_const 的实现：在type_traits头文件中定义，使用偏特化分别继承自 false_type 和 true_type
    template<typename>
    struct is_const
        : public false_type { };

    template<typename _Tp>
    struct is_const<_Tp const>
        : public true_type { };

    // true_type 和 false_type 的实现：两者都是 integral_constant模板 实例化类，见 integral_constant
    /// The type used as a compile-time boolean with true value.
    typedef integral_constant<bool, true>     true_type;

    /// The type used as a compile-time boolean with false value.
    typedef integral_constant<bool, false>    false_type;

    // integral_constant 的实现：在type_traits头文件中定义，可以看到模板参数 template<typename _Tp, _Tp __v>
    //      在 false_type 和 true_type 中，_Tp 为 bool ，__v 分别被声明为对应的 false 和 true
    //      则：static constexpr _Tp value = __v; 的值就是对应的 true或false，也就是 is_const<T>::value 的值
    template<typename _Tp, _Tp __v>
    struct integral_constant
    {
      static constexpr _Tp                  value = __v;
      typedef _Tp                           value_type;
      typedef integral_constant<_Tp, __v>   type;
      constexpr operator value_type() const noexcept { return value; }
#if __cplusplus > 201103L
#define __cpp_lib_integral_constant_callable 201304
      constexpr value_type operator()() const noexcept { return value; }
#endif
    };

    template<typename _Tp, _Tp __v>
    constexpr _Tp integral_constant<_Tp, __v>::value;

    */

    /**
     * 7、双重模板参数
     *      双重模板参数的意义在于：可以通过指定容器的类型来声明 Stack 类模板，无需重新指定容器元素的类型
     *      例如：Stack<int, std::vector> vStack; 
     *           如果不使用双重模板：Stack<int,std::vector<int>> vStack;
     * 双重模板参数是类模板或别名模板的占位符，但函数模板或变量模板没有相应的占位符(所以只能用于做类模板)。
     */

    // 注意，这里template<typename Elem> class Cont = std::deque 声明的是一个类
    template <typename T,
              template <typename Elem,
                        typename Alloc = std::allocator<Elem>>
              class Cont = std::deque> // cpp11
    class MyStack1
    {
    public:
        MyStack1();

    private:
        Cont<T> m_container;
    };

    // 省略Elem、Alloc
    template <typename T, template <typename, typename> class Cont>
    inline MyStack1<T, Cont>::MyStack1()
    {
        if (std::is_same_v<decltype(m_container), std::vector<T>>)
        {
            std::cout << "container is vector<T>" << std::endl;
        }
    }

    // 在cpp17前：
    //      1、class不能用typename代替 
    //      2、Alloc不能省略，因为std::deque的声明需要Alloc。
    // 其次是Elem没有使用到，所以可以省略
    template <typename T, template <typename> typename Cont = std::deque> // since cpp17
    class MyStack2
    {
    public:
        MyStack2();

    private:
        Cont<T> m_container;
    };

    template <typename T, template <typename> typename Cont>
    inline MyStack2<T, Cont>::MyStack2()
    {
        if (std::is_same_v<decltype(m_container), std::vector<T>>)
        {
            std::cout << "container is vector<T>" << std::endl;
        }
    }

};